package com.fivefu.core.skyeye.event.services.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fivefu.base.common.utils.str.StrUtils;
import com.fivefu.core.data.services.DbFfskyeyeChannelService;
import com.fivefu.core.skyeye.event.entity.DbAiScene;
import com.fivefu.core.skyeye.event.entity.DbAiSceneCamera;
import com.fivefu.core.skyeye.event.entity.DbAiSceneNode;
import com.fivefu.core.skyeye.event.entity.DbAiSceneType;
import com.fivefu.core.skyeye.event.properties.RedisStringStreamBean;
import com.fivefu.core.skyeye.event.services.*;
import com.fivefu.core.skyeye.event.services.callable.EditOnOff;
import com.fivefu.core.skyeye.event.vo.SceneCameraVo;
import com.fivefu.core.skyeye.event.vo.SceneVo;
import com.fivefu.core.skyeye.event.vo.UrlChannelVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
public class SceneServiceImpl implements SceneService {

    @Autowired
    private DbAiSceneService dbAiSceneService;
    @Autowired
    private DbAiSceneCameraService dbAiSceneCameraService;
    @Autowired
    private DbAiSceneNodeService dbAiSceneNodeService;
    @Autowired
    private DbAiSceneTypeService dbAiSceneTypeService;
    @Autowired
    private MimirEventService mimirEventService;
    @Autowired
    private DbFfskyeyeChannelService dbFfskyeyeChannelService;
    @Autowired
    private DbAiSceneCameraImageService dbAiSceneCameraImageService;

    @Autowired
    private RedisStringStreamBean redisStringStreamBean;

    /**
     * 查询机器切片识别探头数量
     * @param id 场景编号
     * @return
     */
    @Override
    public Map<String, Object> querySceneNodeCount(Long id){
        DbAiSceneCamera sceneCamera = dbAiSceneCameraService.querySceneCameraByID(id);
        if (sceneCamera != null){
            Map<String, Object> map = formatSceneCamera(sceneCamera);

            Long sliceNode = sceneCamera.getSliceNode();//切片机器编号
            Long eventNode = sceneCamera.getEventNode();//识别机器编号
            if (sliceNode != null){
                Integer count = dbAiSceneNodeService.querySceneNodeCount(sliceNode);//切片机器数量
                map.put("sliceCount",count);
            }
            if (eventNode != null){
                Integer count = dbAiSceneNodeService.querySceneNodeCount(eventNode);//识别机器数量
                map.put("eventCount",count);
            }
            return map;
        }
        return null;
    }

    /**
     * 获取场景下探头详情
     * @param sceneCamera 场景探头信息
     * @return
     */
    public Map<String, Object> formatSceneCamera(DbAiSceneCamera sceneCamera){
        Map<String, Object> map = dbAiSceneCameraService.formatSceneCamera(sceneCamera);

        //持续时长
        DbAiScene scene = dbAiSceneService.querySceneByCode(sceneCamera.getSceneId());
        map.put("continuedTime",scene.getContinuedTime());
        //切片
        if (sceneCamera.getSliceNode() != null){
            Map<String, Object> map1 = dbAiSceneNodeService.querySceneNodeDetail(sceneCamera.getSliceNode());
            String nodeName = map1.get("nodeName").toString();//切片节点
            String nodeType = map1.get("nodeType").toString();//节点类型
            String nodeCount = map1.get("nodeCount").toString();//最大数量
            map.put("sliceCount",Integer.valueOf(nodeCount));
            if (nodeType.equals("slice")){
                map.put("sliceType","切片");
            }
            map.put("sliceNodeName",nodeName);//切片节点

        }
        //识别
        if (sceneCamera.getEventNode() != null){
            Map<String, Object> map1 = dbAiSceneNodeService.querySceneNodeDetail(sceneCamera.getEventNode());
            String nodeName = map1.get("nodeName").toString();//识别节点
            String nodeType = map1.get("nodeType").toString();//节点类型
            String nodeCount = map1.get("nodeCount").toString();//最大数量
            map.put("eventCount",Integer.valueOf(nodeCount));
            if (nodeType.equals("event")){
                map.put("eventType","识别");
            }
            map.put("eventNodeName",nodeName);//识别节点
        }
        if (StrUtils.isNotNull(sceneCamera.getCvParam())){//识别参数
            String sceneId = sceneCamera.getSceneId();
            DbAiScene dbAiScene = dbAiSceneService.querySceneByCode(sceneId);
            String cvType = dbAiScene.getCvType();
            DbAiSceneType dbAiSceneType = dbAiSceneTypeService.querySceneTypeByCvId(Integer.valueOf(cvType));
            String cvTypeParam = dbAiSceneType.getCvParam();//识别参数特殊字段
            map.put("cvTypeParam",cvTypeParam);
        }
        //历史识别绘制图
        String historyImg = dbAiSceneCameraImageService.getSceneCameraImageById(String.valueOf(sceneCamera.getId()));
        map.put("historyImg",historyImg);
        return map;
    }

    /**
     * 分页获取场景探头列表
     * @param cameraCode 场景唯一标识
     * @param page
     * @param limit
     * @return
     */
    @Override
    public Page<Map<String, Object>> querySceneCameraByPage(String cameraCode, Integer page, Integer limit) {
        Page<Map<String,Object>> backPage = new Page<>();
        List<Map<String, Object>> backList = new ArrayList<>();

        Page<DbAiSceneCamera> iPage = dbAiSceneCameraService.querySceneCameraByPage(cameraCode, page, limit);
        List<DbAiSceneCamera> list = iPage.getRecords();
        list.forEach(sceneCamera -> {
            Map<String, Object> map = formatSceneCamera(sceneCamera);
            backList.add(map);
        });

        backPage.setRecords(backList);
        backPage.setTotal(iPage.getTotal());

        return backPage;
    }

    /**
     * 获取场景探头详情
     * @param sceneId 场景编号
     * @param cameraId 探头编号
     * @return
     */
    @Override
    public Map<String, Object> querySceneCameraDetail(String sceneId, String cameraId) {
        //根据场景探头编号获取详情
        DbAiSceneCamera sceneCamera = dbAiSceneCameraService.getSceneCamera(sceneId, cameraId);
        if (sceneCamera != null){
            Map<String, Object> map = formatSceneCamera(sceneCamera);
            return map;
        }
        return null;
    }

    /**
     * 开关场景探头
     * @param sceneCode 场景唯一标识
     * @param cameraList 探头唯一标识
     * @param enable 是否开启(0:否 1:是)
     * @return
     */
    @Override
    public String onOffSceneCamera(String sceneCode,List<String> cameraList,Integer enable){
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        CompletableFuture.supplyAsync(new EditOnOff(sceneCode, cameraList, enable, dbAiSceneService, dbAiSceneCameraService, dbAiSceneTypeService, mimirEventService, dbFfskyeyeChannelService,dbAiSceneNodeService,redisStringStreamBean.getStreamType()),executorService).whenComplete((result, e) ->{

        }).exceptionally((e) -> {
            //抛出异常
            //  System.out.println("exception " + e);
            return "exception";
        });
        return null;
    }

    /**
     * 删除场景下探头
     * @param id 场景探头编号
     * @param userid 操作用户编号
     */
    @Override
    public void deleteSceneCamera(Long id,String userid) {
        DbAiSceneCamera dbAiSceneCamera = dbAiSceneCameraService.querySceneCameraByID(id);

        //删除场景下探头
        dbAiSceneCameraService.deleteSceneCamera(id,userid);

        //判断
        boolean equals = dbAiSceneCamera.getEnable().equals(0);
        List<String> cameraList = new ArrayList<>();
        if (! equals){
            cameraList.add(dbAiSceneCamera.getCameraId());
            onOffSceneCamera(dbAiSceneCamera.getSceneId(),cameraList,0);
        }
    }

    /**
     * 编辑场景探头
     * @param sceneCameraVo 场景探头信息
     * @param userid 操作用户编号
     */
    @Override
    public boolean editSceneCamera(SceneCameraVo sceneCameraVo, String userid) {
        DbAiSceneCamera sceneCamera = new DbAiSceneCamera();
        sceneCamera.setIsDelete(0);
        sceneCamera.setSceneId(sceneCameraVo.getSceneId());//场景编号
        sceneCamera.setCameraId(sceneCameraVo.getCameraId());//探头编号
        sceneCamera.setEnable(sceneCameraVo.getEnable());//是否开启
        sceneCamera.setScope(sceneCameraVo.getScope());//是否限定区域
        sceneCamera.setScopeArea(sceneCameraVo.getScopeArea());//区域
        sceneCamera.setCameraValidTime(sceneCameraVo.getCameraValidTime());//探头有效时间
        sceneCamera.setSliceNode(sceneCameraVo.getSliceNode());//切片机器编号
        sceneCamera.setEventNode(sceneCameraVo.getEventNode());//识别机器编号
        sceneCamera.setCvParam(sceneCameraVo.getCvParam());//识别参数
        sceneCamera.setImage(sceneCameraVo.getImage());//缩略图

        Integer sliceMaxNum = dbAiSceneNodeService.querySceneNodeCount(sceneCameraVo.getSliceNode());//切片定义最大节点数
        Integer eventMaxNum = dbAiSceneNodeService.querySceneNodeCount(sceneCameraVo.getEventNode());//识别定义最大节点数

        boolean isEditSceneCamera = dbAiSceneCameraService.isEditSceneCamera(sceneCamera, sliceMaxNum, eventMaxNum);//是否满足节点机器数量
        if (isEditSceneCamera){
            boolean change = dbAiSceneCameraService.editSceneCamera(sceneCamera, userid);
            if (change){
                List<String> cameraList = new ArrayList<>();
                cameraList.add(sceneCamera.getCameraId());
                onOffSceneCamera(sceneCamera.getSceneId(),cameraList,sceneCamera.getEnable());
            }
            return true;
        }
        return false;
    }

    /**
     * 开启、关闭场景探头
     * @param id 场景探头编号
     * @param userid 用户编号
     * @param enable 是否启用(0:否 1:启用)
     * @return
     */
    @Override
    public void enableSceneCamera(Long id, String userid, Integer enable) {
        DbAiSceneCamera dbAiSceneCamera = dbAiSceneCameraService.querySceneCameraByID(id);
        dbAiSceneCameraService.enableSceneCamera(id,userid,enable);

        boolean change = dbAiSceneCamera.getEnable().equals(enable);
        if (! change){
            List<String> cameraList = new ArrayList<>();
            cameraList.add(dbAiSceneCamera.getCameraId());
            onOffSceneCamera(dbAiSceneCamera.getSceneId(),cameraList,enable);
        }
    }


    /**
     * 推送有切片的探头流地址
     * @param protocal      取流协议(共同协议：rtsp,rtmp,hls,海康额外有：hik,ws,大华额外有：FLV_HTTP)
     * @param subType       码流类型(0:主码流,1:辅流1,2:辅流2。默认为0主码流)只有大华有
     * @return
     */
    @Override
    public List<UrlChannelVo> getSceneCameraList(String protocal, Integer subType) {
        List<UrlChannelVo> resultList = new ArrayList<>();
        //获取开启的场景数据集
        List<DbAiScene> sceneList = dbAiSceneService.queryOpenScene();
        if (null == sceneList || sceneList.size() <= 0) {
            return resultList;
        }
        List<String> sceneCodeList = sceneList.stream().map(k -> k.getCode()).collect(Collectors.toList());
        //查询所有的切片信息获取切片编号
        List<DbAiSceneNode> sceneNodes = dbAiSceneNodeService.getAllSlice();
        if (null == sceneNodes || sceneNodes.size() <= 0) {
            return resultList;
        }
        List<String> sliceCodeList = sceneNodes.stream().map(k -> k.getId().toString()).collect(Collectors.toList());

        //获取开启的探头
        List<DbAiSceneCamera> cameraList = dbAiSceneCameraService.getOpenCamera(sceneCodeList, sliceCodeList);

        Set<String> resultSet = cameraList.stream().map(k -> k.getCameraId()).collect(Collectors.toSet());
        String channelCodes = String.join(",",resultSet);
        resultList = getAllCameraUrl(channelCodes, protocal, subType);

        return resultList;
    }

    /**
     * 调用data项目中的查询多个通道流地址
     * @param channelCode   有切片的探头编号
     * @param protocal      取流协议(共同协议：rtsp,rtmp,hls,海康额外有：hik,ws,大华额外有：FLV_HTTP)
     * @param subType       码流类型(0:主码流,1:辅流1,2:辅流2。默认为0主码流)只有大华有
     * @return
     */
    public List<UrlChannelVo> getAllCameraUrl(String channelCode,String protocal,Integer subType){
        List<UrlChannelVo> resultList = new ArrayList<>();
        try {
            if(StrUtils.isNull(channelCode) ){
                return resultList;
            }

            Map<String,String> resultMap = dbFfskyeyeChannelService.getUrlBychannelCodes(channelCode,protocal,subType);
            Set<String> cameraSet = resultMap.keySet();
            cameraSet.forEach(cameraCode -> {
                UrlChannelVo urlChannelVo = new UrlChannelVo();
                urlChannelVo.setChannelCode(cameraCode);
                urlChannelVo.setUrl(resultMap.get(cameraCode));
                resultList.add(urlChannelVo);
            });
            //获取返回的url
            return resultList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultList;
    }

    /**
     * 查询探头视频流地址
     * @param cameraCode 探头唯一标识
     * @return
     */
    public String getCameraUrl(String cameraCode){
        try {
            if(StrUtils.isNull(cameraCode) ){
                return null;
            }
            String url = dbFfskyeyeChannelService.queryUrlByChannelCode(cameraCode,"rtsp",null);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 编辑场景
     * @param sceneVo 场景信息
     * @param userid 操作用户编号
     */
    @Override
    public Long editScene(SceneVo sceneVo, String userid) {
        Long id = sceneVo.getId();
        DbAiScene dbAiScene = new DbAiScene();
        dbAiScene.setIsDelete(0);
        dbAiScene.setName(sceneVo.getName());//场景名称
        dbAiScene.setDescription(sceneVo.getDescription());//场景描述
        dbAiScene.setEnable(sceneVo.getEnable());//是否开启
        dbAiScene.setContinuedTime(sceneVo.getContinuedTime());//持续时长
        dbAiScene.setSceneValidTime(sceneVo.getSceneValidTime());//场景有效时间
        dbAiScene.setCvParam(sceneVo.getCvParam());//识别参数
        dbAiScene.setSceneArea(sceneVo.getSceneArea());//是否绘制识别区域;0:否,1:是
        dbAiScene.setCvType(sceneVo.getCvType());//识别类型
        dbAiScene.setId(id);

        DbAiScene oldScene = null;
        if (id != null){
            oldScene = dbAiSceneService.querySceneByID(id);
        }
        id = dbAiSceneService.editScene(dbAiScene, userid);

        if (oldScene != null){
            boolean enable = sceneVo.getEnable().equals(oldScene.getEnable());//是否开启有没有发生改变
            boolean continuedTime = sceneVo.getContinuedTime().equals(oldScene.getContinuedTime());//持续时长有没有发生改变
            if (! enable || !continuedTime){
                //开启或关闭该场景下的探头
                onOffSceneCamera(oldScene.getCode(),null,sceneVo.getEnable());
            }
        }

        return id;
    }

    /**
     * 开启、关闭场景
     * @param id 场景编号
     * @param userid 用户编号
     * @param enable 是否启用(0:否 1:是)
     */
    @Override
    public void enableScene(Long id,String userid,Integer enable) {
        DbAiScene aiScene = dbAiSceneService.querySceneByID(id);

        dbAiSceneService.enableScene(id, userid, enable);

        //判断
        boolean equals = aiScene.getEnable().equals(enable);
        if (! equals){
            //开启或关闭该场景下的探头
            onOffSceneCamera(aiScene.getCode(),null,enable);
        }
    }

    /**
     * 删除场景
     * @param id 场景编号
     * @param userid 用户编号
     */
    @Override
    public void deleteScene(Long id,String userid) {
        DbAiScene aiScene = dbAiSceneService.querySceneByID(id);
        dbAiSceneService.deleteScene(id , userid);
        //判断
        boolean equals = aiScene.getEnable().equals(0);
        List<String> cameraList = dbAiSceneCameraService.queryCameraCodeBySceneID(aiScene.getCode(),1);
        if ( !equals && cameraList != null && cameraList.size() > 0){
            //关闭该场景下的探头
            onOffSceneCamera(aiScene.getCode(),cameraList,0);
        }
    }

    /**
     * 获取切片编号
     * @param sizeNum 需要的切片数量
     * @return
     */
    public Long getSliceNode(Integer sizeNum){
        List<DbAiSceneNode> allSlice = dbAiSceneNodeService.getAllSlice();
        List<Long> sliceList = allSlice.stream().map(k -> k.getId()).collect(Collectors.toList());
        for (Long sliceId : sliceList) {
            Integer sliceMaxNum = dbAiSceneNodeService.querySceneNodeCount(sliceId);//切片定义最大节点数
            Integer sliceNum = dbAiSceneCameraService.querySliceNodeNum(sliceId);//已使用的切片数量
            Integer sliceCount = sliceMaxNum -sliceNum;//切片剩余数量
            if (sliceCount >= sizeNum){
                return sliceId;
            }
        }
        return null;
    }

    /**
     * 获取识别编号
     * @param sizeNum 需要的识别数量
     * @return
     */
    public Long getEventNode(Integer sizeNum){
        List<DbAiSceneNode> allEvent = dbAiSceneNodeService.getAllEvent();
        List<Long> evnetList = allEvent.stream().map(k -> k.getId()).collect(Collectors.toList());
        for (Long eventId : evnetList) {
            Integer eventMaxNum = dbAiSceneNodeService.querySceneNodeCount(eventId);//识别定义最大节点数
            Integer eventNum = dbAiSceneCameraService.queryEventNodeNum(eventId);//已使用的识别数量
            Integer eventCount = eventMaxNum -eventNum;//切片剩余数量
            if (eventCount >= sizeNum){
                return eventId;
            }
        }
        return null;
    }
}

